# -*- coding: utf-8 -*-
"""
文件服务
专门处理文件上传、OSS操作等功能
"""

from typing import Dict, Any
from fastapi import UploadFile
import oss2
import uuid
import time
from src.utils.logger import app_logger as logger
from src.schemas.detection import UploadImageResponse
from src.config.settings import get_settings


class FileService:
    """文件服务类"""
    
    def __init__(self):
        self.settings = get_settings()
    
    @property
    def OSS_ACCESS_KEY_ID(self):
        return self.settings.OSS_ACCESS_KEY_ID
    
    @property
    def OSS_ACCESS_KEY_SECRET(self):
        return self.settings.OSS_ACCESS_KEY_SECRET
    
    @property
    def OSS_ENDPOINT(self):
        return self.settings.OSS_ENDPOINT
    
    @property
    def OSS_BUCKET_NAME(self):
        return self.settings.OSS_BUCKET_NAME
    
    @property
    def OSS_STORAGE_PATH(self):
        return self.settings.OSS_STORAGE_PATH
    
    @property
    def OSS_AVATAR_PATH(self):
        return self.settings.OSS_AVATAR_PATH
    
    @property
    def OSS_HAIR_PATH(self):
        return self.settings.OSS_HAIR_PATH
    
    async def upload_to_oss(
        self,
        user_id: int, 
        image_file: UploadFile, 
        storage_path: str = None
    ) -> UploadImageResponse:
        """
        上传文件到阿里云OSS
        
        Args:
            user_id: 用户ID
            image_file: 上传的文件
            storage_path: 自定义存储路径，如果不提供则使用默认路径
            
        Returns:
            UploadImageResponse: 包含图片URL和OSS路径的响应
            
        Raises:
            Exception: 上传失败时抛出异常
        """
        try:
            # 验证文件类型
            if not image_file.content_type or not image_file.content_type.startswith('image/'):
                raise Exception("只支持图片文件")
            
            # 验证文件大小 (10MB)
            if image_file.size and image_file.size > 10 * 1024 * 1024:
                raise Exception("图片文件大小不能超过10MB")
            
            # 生成唯一文件名
            file_extension = self._get_file_extension(image_file.filename)
            filename = self._generate_filename(user_id, file_extension)
            
            # 确定存储路径
            if storage_path is None:
                storage_path = self.OSS_STORAGE_PATH
            
            oss_path = f"{storage_path}{filename}"
            
            # 读取文件内容
            file_content = await image_file.read()
            
            # 上传到OSS
            image_url = await self._upload_to_oss_bucket(oss_path, file_content)
            
            logger.info(f"用户 {user_id} 上传文件到OSS成功: {image_url}")
            
            return UploadImageResponse(
                image_url=image_url,
                oss_path=oss_path
            )
            
        except Exception as e:
            logger.error(f"文件上传到OSS失败: {str(e)}")
            raise Exception(f"文件上传到OSS失败: {str(e)}")
    
    def _get_file_extension(self, filename: str) -> str:
        """
        获取文件扩展名
        
        Args:
            filename: 文件名
            
        Returns:
            str: 文件扩展名，默认为jpg
        """
        if filename and '.' in filename:
            return filename.split('.')[-1].lower()
        return 'jpg'
    
    def _generate_filename(self, user_id: int, file_extension: str) -> str:
        """
        生成唯一文件名
        
        Args:
            user_id: 用户ID
            file_extension: 文件扩展名
            
        Returns:
            str: 唯一文件名
        """
        timestamp = int(time.time())
        random_id = str(uuid.uuid4())[:8]
        return f"{user_id}_{timestamp}_{random_id}.{file_extension}"
    
    async def _upload_to_oss_bucket(self, oss_path: str, file_content: bytes) -> str:
        """
        上传文件到OSS存储桶
        
        Args:
            oss_path: OSS存储路径
            file_content: 文件内容
            
        Returns:
            str: 完整的图片URL
            
        Raises:
            Exception: 上传失败时抛出异常
        """
        try:
            # 创建OSS认证和存储桶对象
            auth = oss2.Auth(
                self.OSS_ACCESS_KEY_ID, 
                self.OSS_ACCESS_KEY_SECRET
            )
            bucket = oss2.Bucket(
                auth, 
                self.OSS_ENDPOINT, 
                self.OSS_BUCKET_NAME
            )
            
            # 上传文件
            result = bucket.put_object(oss_path, file_content)
            
            if result.status != 200:
                raise Exception(f"OSS上传失败，状态码: {result.status}")
            
            # 由于bucket设置为私有，需要生成签名URL
            # 生成7天有效的签名URL
            signed_url = bucket.sign_url('GET', oss_path, 7 * 24 * 3600)
            
            return signed_url
            
        except Exception as e:
            logger.error(f"OSS存储桶上传失败: {str(e)}")
            raise Exception(f"OSS存储桶上传失败: {str(e)}")
    
    @staticmethod
    def validate_image_file(image_file: UploadFile) -> Dict[str, Any]:
        """
        验证图片文件
        
        Args:
            image_file: 上传的文件
            
        Returns:
            Dict[str, Any]: 验证结果
            
        Raises:
            Exception: 验证失败时抛出异常
        """
        try:
            # 验证文件类型
            if not image_file.content_type or not image_file.content_type.startswith('image/'):
                raise Exception("请上传图片文件")
            
            # 验证文件大小 (10MB)
            max_size = 10 * 1024 * 1024
            if image_file.size and image_file.size > max_size:
                raise Exception("图片文件大小不能超过10MB")
            
            # 验证文件名
            if not image_file.filename:
                raise Exception("文件名不能为空")
            
            return {
                "valid": True,
                "filename": image_file.filename,
                "content_type": image_file.content_type,
                "size": image_file.size
            }
            
        except Exception as e:
            logger.error(f"文件验证失败: {str(e)}")
            raise Exception(f"文件验证失败: {str(e)}")
    
    def extract_oss_path_from_url(self, image_url: str) -> str:
        """
        从完整URL中提取OSS路径
        
        Args:
            image_url: 完整的图片URL
            
        Returns:
            str: OSS存储路径
        """
        try:
            # 检查所有可能的OSS路径前缀
            oss_paths = [
                self.OSS_STORAGE_PATH,  # ewentianshi/hair/
                self.OSS_AVATAR_PATH,   # ewentianshi/avatar/
                self.OSS_HAIR_PATH,     # ewentianshi/hair/
                "ewentianshi/"          # 通用前缀
            ]
            
            # URL解码处理（处理%2F等编码）
            import urllib.parse
            decoded_url = urllib.parse.unquote(image_url)
            
            for oss_path in oss_paths:
                if oss_path in decoded_url:
                    start = decoded_url.find(oss_path)
                    # 提取从oss_path开始到查询参数之前的部分
                    extracted_path = decoded_url[start:]
                    # 移除查询参数（如?OSSAccessKeyId=...）
                    if '?' in extracted_path:
                        extracted_path = extracted_path.split('?')[0]
                    return extracted_path
            
            # 如果没有找到已知路径，尝试从域名后提取
            if self.OSS_BUCKET_NAME in decoded_url and self.OSS_ENDPOINT in decoded_url:
                # 格式: https://bucket.endpoint/path
                domain_part = f"{self.OSS_BUCKET_NAME}.{self.OSS_ENDPOINT}/"
                if domain_part in decoded_url:
                    start = decoded_url.find(domain_part) + len(domain_part)
                    extracted_path = decoded_url[start:]
                    if '?' in extracted_path:
                        extracted_path = extracted_path.split('?')[0]
                    return extracted_path
            
            logger.warning(f"无法从URL提取OSS路径: {image_url}")
            return ""
        except Exception as e:
            logger.warning(f"提取OSS路径失败: {str(e)}")
            return ""
    
    def generate_signed_url(self, oss_path: str, expire_seconds: int = 24 * 3600) -> str:
        """
        生成OSS文件的签名访问URL
        
        Args:
            oss_path: OSS存储路径
            expire_seconds: 签名有效期（秒），默认24小时
            
        Returns:
            str: 签名URL
        """
        try:
            # 创建OSS认证和存储桶对象
            auth = oss2.Auth(
                self.OSS_ACCESS_KEY_ID, 
                self.OSS_ACCESS_KEY_SECRET
            )
            bucket = oss2.Bucket(
                auth, 
                self.OSS_ENDPOINT, 
                self.OSS_BUCKET_NAME
            )
            
            # 生成签名URL
            signed_url = bucket.sign_url('GET', oss_path, expire_seconds)
            return signed_url
            
        except Exception as e:
            logger.error(f"生成签名URL失败: {str(e)}")
            # 如果签名失败，返回原始路径
            return f"https://{self.OSS_BUCKET_NAME}.{self.OSS_ENDPOINT}/{oss_path}"
    
    def regenerate_signed_url_from_url(self, image_url: str, expire_seconds: int = 7 * 24 * 3600) -> str:
        """
        从完整的图片URL重新生成签名URL
        
        Args:
            image_url: 完整的图片URL
            expire_seconds: 签名有效期（秒），默认7天
            
        Returns:
            str: 新的签名URL
        """
        try:
            # 提取OSS路径
            oss_path = self.extract_oss_path_from_url(image_url)
            if not oss_path:
                logger.warning(f"无法从URL提取OSS路径: {image_url}")
                return image_url
            
            # 生成新的签名URL
            return self.generate_signed_url(oss_path, expire_seconds)
            
        except Exception as e:
            logger.error(f"重新生成签名URL失败: {str(e)}")
            return image_url